// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#include "coin2/exchange/prex_futures/api_order/native_rest_async_asio_subscriber.h"

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include <boost/algorithm/string.hpp>

#include "coin2/base/log.h"

namespace coin2::exchange::prex_futures::api_order {

const std::string& PrexFuturesRestAsyncAsioSubscriber::next_native_symbol() {
  if (native_symbol_list_.empty()) {
    for (const auto& s : native_symbols()) {
      native_symbol_list_.push_back(s);
    }
  }
  CHECK(!native_symbol_list_.empty());
  const int idx = std::rand() % native_symbol_list_.size();
  return native_symbol_list_.at(idx);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryWallet() {
  auto http_context = rest_client()->QueryWallet();
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryMargin() {
  auto http_context = rest_client()->QueryMargin();
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryPosition() {
  auto http_context = rest_client()->QueryPosition();
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryOpenOrders() {
  auto http_context = rest_client()->QueryOpenOrders();
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryAllOrders() {
  RestParams params;
  params.set_symbol(next_native_symbol());
  params.set_count("500");
  auto http_context = rest_client()->QueryOrders(params);
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryFill() {
  RestParams params;
  params.set_symbol(next_native_symbol());
  params.set_count("50");
  auto http_context = rest_client()->QueryExecution(params);
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryHistConversions() {
  RestParams params;
  params.set_count("50");
  auto http_context = rest_client()->QueryHistConversions(params);
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void PrexFuturesRestAsyncAsioSubscriber::QueryOrderByIds(
    const std::string& exchange_order_ids,
    const std::string& proc_order_ids) {
  int64_t ts = GetCurrentTimestamp();
  if (ts - last_query_order_by_ids_ts_ < 5'000'000'000LL) {
    SPDLOG_WARN("query order by ids to often!!");
    return;
  }

  RestParams params;
  params.set_order_id(exchange_order_ids);
  auto http_context = rest_client()->QueryOrdersByIds(params);
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_context->extra_info = proc_order_ids;
  http_driver_->PostAsyncContext(http_context);
  last_query_order_by_ids_ts_ = ts;
}

void PrexFuturesRestAsyncAsioSubscriber::SubscribeAsync() {
  // Query Wallet
  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryWallet, this),
      config_.query_account_balance_period_sec(),
      "query_wallet",
      3);

  // Query Margin
  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryMargin, this),
      config_.query_account_balance_period_sec(),
      "query_margin",
      4);

  // Query Position
  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryPosition, this),
      config_.query_account_position_period_sec(),
      "query_position",
      5);

  if (is_readonly_order_system_) {
    return;
  }

  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryOpenOrders, this),
      config_.query_open_orders_period_sec(),
      "query_open_orders",
      6);

  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryAllOrders, this),
      config_.query_cancel_orders_period_sec(),
      "query_all_orders",
      7);

  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryFill, this),
      config_.query_fill_period_sec(),
      "query_fill",
      8);

  AddPeriodicTask(
      std::bind(&PrexFuturesRestAsyncAsioSubscriber::QueryHistConversions, this),
      config_.query_conversions_period_sec(),
      "query_hist_conversions",
      9);

  mb_->Subscribe("prex_futures/query_order_by_ids", [this](const std::string&, void* data) {
    std::string param = *(reinterpret_cast<std::string*>(data));
    std::vector<std::string> info;
    boost::split(info, param, boost::is_any_of("|"));
    if (info.size() != 2 || info[0].empty() || info[1].empty()) {
      LOG(ERROR) << "ignored check zombie orders, invalid info";
      return;
    }
    this->QueryOrderByIds(info[0], info[1]);
  });
}

}  // namespace coin2::exchange::prex_futures::api_order
